package com.example.dmy.bootoauth.config;

import cn.hutool.crypto.digest.DigestUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

import javax.sql.DataSource;
import javax.swing.plaf.synth.SynthOptionPaneUI;

/**
 * @Author: daimengying
 * @Date: 2019/1/5 09:38
 * @Description:授权服务器
 * 此处将客户端信息放到了内存中，生产中可以配置到数据库中。
 * token的存储一般选择使用Redis，一是性能比较好，二是自动过期的机制，符合token的特性。
 *
 * redirect_uri任意
 * http://localhost:9000/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://baidu.com?scopes=all
 *
 * POST:
 * http://localhost:8080/oauth2/access_token?
 * client_id=client&client_secret=123456
 * &grant_type=authorization_code&redirect_uri=http://baidu.com&code={code}
 */
@Configuration
@EnableAuthorizationServer
@Order(6)
public class ServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    DataSource dataSource;

    @Autowired
    private  PasswordEncoder passwordEncoder;

    private static final String DEMO_RESOURCE_ID = "order";

//    @Autowired
//    RedisConnectionFactory redisConnectionFactory;

    @Bean
    public ClientDetailsService clientDetails() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 配置token获取和验证时的策略 (Spring Security安全表达式),可以表单提交
        security.tokenKeyAccess( "permitAll()").checkTokenAccess("isAuthenticated()").allowFormAuthenticationForClients();
    }

    /**
     * @param clients
     * @throws Exception
     *
     * 客户端模式token请求：
     * http://127.0.0.1:9000/oauth/token?grant_type=client_credentials&client_id=client1&client_secret=123456
     *
     * DROP TABLE IF EXISTS `oauth_client_details`;
    CREATE TABLE `oauth_client_details` (
    `client_id` varchar(48) NOT NULL,
    `resource_ids` varchar(256) DEFAULT NULL,
    `client_secret` varchar(256) DEFAULT NULL,
    `scope` varchar(256) DEFAULT NULL,
    `authorized_grant_types` varchar(256) DEFAULT NULL,
    `web_server_redirect_uri` varchar(256) DEFAULT NULL,
    `authorities` varchar(256) DEFAULT NULL,
    `access_token_validity` int(11) DEFAULT NULL,
    `refresh_token_validity` int(11) DEFAULT NULL,
    `additional_information` varchar(4096) DEFAULT NULL,
    `autoapprove` varchar(256) DEFAULT NULL,
    PRIMARY KEY (`client_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //客户端信息存储数据库
        clients.withClientDetails(clientDetails());
        String secret = passwordEncoder.encode("123456");//$2a$10$af9lXckfuaHRCO7vEOJcgOD9kXvZ5zXD4g/I6gFfd1mHfOifsaHIu
        System.out.println("secret>>>"+secret);
//        clients.inMemory() // 客户端信息存储在内存中
//                .withClient("client") //第三方应用用户名
//                // secret密码配置从 Spring Security 5.0开始必须以 {加密方式}+加密后的密码 这种格式填写
//
//                /* *   当前版本5新增支持加密方式：
//                 *   bcrypt - BCryptPasswordEncoder (Also used for encoding)
//                 *   ldap - LdapShaPasswordEncoder
//                 *   MD4 - Md4PasswordEncoder
//                 *   MD5 - new MessageDigestPasswordEncoder("MD5")
//                 *   noop - NoOpPasswordEncoder 明文形式,不需要设置passwordEncoder
//                 *   pbkdf2 - Pbkdf2PasswordEncoder
//                 *   scrypt - SCryptPasswordEncoder
//                 *   SHA-1 - new MessageDigestPasswordEncoder("SHA-1")
//                 *   SHA-256 - new MessageDigestPasswordEncoder("SHA-256")
//                 *   sha256 - StandardPasswordEncoder*/
//                //以下三种加密方式都可以
//                .secret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("123456"))
////                .secret(secret)
////                .secret("{noop}123456")
//                .scopes("all") //相当于oauth的权限，这里配置了，请求里的必须和这里匹配
//                .resourceIds(DEMO_RESOURCE_ID)
//                .autoApprove(true)
//                .authorizedGrantTypes("authorization_code", "refresh_token")//授权码模式
//                .accessTokenValiditySeconds(1200) //token有效期
//                .refreshTokenValiditySeconds(50000);//强制清除客户端持有的token
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 配置tokenStore，保存到redis缓存中
//        endpoints.authenticationManager(authenticationManager)
//                .tokenStore(new MyRedisTokenStore(redisConnectionFactory))
//                // 不添加userDetailsService，刷新access_token时会报错
//                .userDetailsService(userDetailsService);
        // 使用最基本的InMemoryTokenStore生成token
        endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore()).reuseRefreshTokens(false);
    }

    // 使用最基本的InMemoryTokenStore生成token
    @Bean
    public TokenStore memoryTokenStore() {
        return new InMemoryTokenStore();
    }
}
